﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using ICSharpCode.SharpZipLib.Zip;

namespace CalcSharp.Core.Controlls
{
    public class HelpReader
    {
        Dictionary<string, int> HelpDB;
        string Zfile;

        /// <summary>
        /// Creates a new instance of the help reader
        /// </summary>
        public HelpReader()
        {
            this.HelpDB = new Dictionary<string, int>();
        }

        /// <summary>
        /// Creates a new instance of the help reader
        /// </summary>
        /// <param name="file">The file to be used as help source. Must be a valid zip file</param>
        public HelpReader(string file): this()
        {
            this.OpenFile(file);
        }

        /// <summary>
        /// Opens a zip file as help.
        /// </summary>
        /// <param name="path">The path of the zip file.</param>
        /// <returns>true on succes, false on error</returns>
        public bool OpenFile(string path)
        {
            if (!File.Exists(path)) return false;
            this.HelpDB.Clear();
            this.Zfile = path;
            try
            {
                ZipFile Zip = new ZipFile(File.OpenRead(this.Zfile));
                for (int i = 0; i < Zip.Count; ++i)
                {
                    if (Zip[i].IsFile && Zip[i].Name.ToLower().EndsWith(".rtf"))
                    {
                        HelpDB.Add(Zip[i].Name.ToLower(), i);
                    }
                }
                Zip.Close();
                return true;
            }
            catch (Exception)
            {
                return false;
            }
        }

        /// <summary>
        /// Checks weather a given path is in the zip file.
        /// </summary>
        /// <param name="contentfile">The file entry to search for</param>
        /// <returns>true if exists, false if not</returns>
        public bool ContentExists(string contentfile)
        {
            if (this.HelpDB.ContainsKey(contentfile.ToLower())) return true;
            return false;
        }

        public bool CommmandHelpExists(string cmd)
        {
            if (this.HelpDB.ContainsKey("cmd/" + cmd.ToLower() + ".rtf")) return true;
            else return false;
        }

        /// <summary>
        /// Lists command helps in the given help file
        /// </summary>
        /// <returns>the list of available command helps</returns>
        public string[] ListCmdHelps()
        {
            List<string> cmdhelps = new List<string>();
            foreach (string key in this.HelpDB.Keys)
            {
                if (key.StartsWith("cmd/")) cmdhelps.Add(key.Replace("cmd/", "").Replace(".rtf", ""));
            }
            return cmdhelps.ToArray();
        }

        /// <summary>
        /// Lists all topics in the gien help file
        /// </summary>
        /// <returns>the list of available topics</returns>
        public string[] ListContents()
        {
            return this.HelpDB.Keys.ToArray();
        }

        public string[] ListTopics()
        {
            string[] ret = HelpDB.Keys.ToArray();
            for (int i = 0; i<ret.Length; i++)
            {
                ret[i] = Path.GetFileName(ret[i]).Replace(".rtf", "");
            }
            return ret;
        }

        private StringBuilder Byte2Chars(byte[] input, int len) //with utf support :)
        {
            StringBuilder sb = new StringBuilder();
            int limit = len;
            int i = 0;
            if (len > input.Length) limit = input.Length;
            while (i < limit)
            {
                if (input[i] < 128)
                {
                    sb.Append((char)input[i]);
                    ++i;
                }
                else if (i + 1 < limit)
                {
                    short val = (short)(input[i + 1] << 4);
                    val += input[i];
                    i += 2;
                    sb.Append((char)val);
                }
            }
            return sb;
        }

        /// <summary>
        /// Gets a help content file
        /// </summary>
        /// <param name="contentfile">the path in the zip</param>
        /// <returns>the content file as string</returns>
        public string GetHelpContent(string contentfile)
        {
            byte[] buffer = new byte[4096];
            int buff;
            StringBuilder sb = new StringBuilder();
            if (!string.IsNullOrEmpty(contentfile))
            {
                if (HelpDB.ContainsKey(contentfile.ToLower()))
                {
                    ZipFile Zip = new ZipFile(File.OpenRead(this.Zfile));
                    int index = HelpDB[contentfile.ToLower()];
                    Stream input = Zip.GetInputStream(index);
                    do
                    {
                        buff = input.Read(buffer, 0, buffer.Length);
                        sb.Append(Byte2Chars(buffer, buff));
                    }
                    while (buff > 0);
                    return sb.ToString();
                }
                else throw new CSException("File not found");
            }
            else throw new ArgumentException("Contentfile can't be null");
        }

        public string GetHelpContentPath(string content)
        {
            foreach (string s in HelpDB.Keys)
            {
                if (s.Contains("/"+content + ".rtf")) return s;
            }
            return null;
        }
    }
}
